//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "Graphics/GLES/opengles.h"

namespace Engine
{
namespace Graphics
{

	struct GLConst
	{
		enum
		{
			MIN_TEXTURE_UNITS	= 8,	// see OpenGL ES 2.0 specification
			MAX_TEXTURE_UNITS	= 64,

			MIN_VERTEX_ATTRIBS	= 8,	// see OpenGL ES 2.0 specification
			MAX_VERTEX_ATTRIBS	= 8,

			SCISSOR_STACK_SIZE	= 8,
			VIEWPORT_STACK_SIZE	= 8,
		};
	};

	
	struct gl_blend_equation
	{
		enum type
		{
			ADD					= gles::GL_FUNC_ADD,				// S+D
			SUB					= gles::GL_FUNC_SUBTRACT,			// S-D
			REV_SUB				= gles::GL_FUNC_REVERSE_SUBTRACT,	// D-S
			MIN					= gles::EXT_blend_minmax::GL_MIN,	// min(S,D)
			MAX					= gles::EXT_blend_minmax::GL_MAX,	// max(S,D)
		};
	};
	

	struct gl_blend_func
	{
		enum type
		{
			ZERO						= gles::GL_ZERO,						// 0
			ONE							= gles::GL_ONE,							// 1
			SRC_COLOR					= gles::GL_SRC_COLOR,					// src
			ONE_MINUS_SRC_COLOR			= gles::GL_ONE_MINUS_SRC_COLOR,			// 1 - src
			DST_COLOR					= gles::GL_DST_COLOR,					// dst
			ONE_MINUS_DST_COLOR			= gles::GL_ONE_MINUS_DST_COLOR,			// 1 - dst
			SRC_ALPHA					= gles::GL_SRC_ALPHA,					// src.a
			ONE_MINUS_SRC_ALPHA			= gles::GL_ONE_MINUS_SRC_ALPHA,			// 1 - src.a
			DST_ALPHA					= gles::GL_DST_ALPHA,					// dst.a
			ONE_MINUS_DST_ALPHA			= gles::GL_ONE_MINUS_DST_ALPHA,			// 1 - dst.a
			CONST_COLOR					= gles::GL_CONSTANT_COLOR,				// cc
			ONE_MINUS_CONST_COLOR		= gles::GL_ONE_MINUS_CONSTANT_COLOR,	// 1 - cc
			CONST_ALPHA					= gles::GL_CONSTANT_ALPHA,				// cc.a
			ONE_MINUS_CONST_ALPHA		= gles::GL_ONE_MINUS_CONSTANT_ALPHA,	// 1 - cc.a
			SRC_ALPHA_SATURATE			= gles::GL_SRC_ALPHA_SATURATE,			// rgb * min( src.a, dst.a ), a * 1
		};
	};


	struct gl_compare
	{
		enum type
		{
			NONE		= gles::GL_NONE,		// (disable)
			NEVER		= gles::GL_NEVER,		// false
			LESS		= gles::GL_LESS,		// <
			EQUAL		= gles::GL_EQUAL,		// ==
			LEQUAL		= gles::GL_LEQUAL,		// <=
			GREATER		= gles::GL_GREATER,		// >
			NOTEQUAL	= gles::GL_NOTEQUAL,	// !=
			GEQUAL		= gles::GL_GEQUAL,		// >=
			ALWAYS		= gles::GL_ALWAYS,		// true
		};
	};


	struct gl_stencil_op
	{
		enum type
		{
			KEEP		= gles::GL_KEEP,
			ZERO		= gles::GL_ZERO,
			REPLACE		= gles::GL_REPLACE,
			INCR		= gles::GL_INCR,
			INCR_WRAP	= gles::GL_INCR_WRAP,
			DECR		= gles::GL_DECR,
			DECR_WRAP	= gles::GL_DECR_WRAP,
			INVERT		= gles::GL_INVERT,
		};
	};



	//
	// Render State
	//
	
	struct RenderState
	{
	// types
	public:

		//
		// Color State
		//
		struct ColorState
		{
			gl_blend_func::type		blendFuncSrc,
									blendFuncDst;
			gl_blend_equation::type	blendMode;
			color4u					blendColor;
			bool					blend;
			bool					maskR,
									maskG,
									maskB,
									maskA;
			///
			ColorState():
				blendFuncSrc(gl_blend_func::ONE), blendFuncDst(gl_blend_func::ZERO),
				blendMode(gl_blend_equation::ADD), blendColor(255), blend(false),
				maskR(true), maskG(true), maskB(true), maskA(true)
			{}

			bool operator == (const ColorState &s) const
			{
				return	blendFuncSrc == s.blendFuncSrc and
						blendFuncDst == s.blendFuncDst and
						All( blendColor == s.blendColor ) and
						blendMode == s.blendMode and
						blend == s.blend and
						maskR == s.maskR and
						maskG == s.maskG and
						maskB == s.maskB and
						maskA == s.maskA;
			}

			void Invalidate ()
			{
				blendColor		= blendColor + color4u(5);
				blendFuncSrc	= gl_blend_func::type(-1);
				blendFuncDst	= gl_blend_func::type(-1);
				blendMode		= gl_blend_equation::type(-1);
				blend			= not blend;
				maskR			= not maskR;
				maskG			= not maskG;
				maskB			= not maskB;
				maskA			= not maskA;
			}

			void Validate ()
			{
				if ( blendFuncSrc == gl_blend_func::type(-1) )
					blendFuncSrc = gl_blend_func::ONE;

				if ( blendFuncDst == gl_blend_func::type(-1) )
					blendFuncDst = gl_blend_func::ZERO;

				if ( blendMode == gl_blend_equation::type(-1) )
					blendMode = gl_blend_equation::ADD;
			}
		};


		//
		// Color State Separate
		//
		struct ColorStateSeparate
		{
			gl_blend_func::type		blendFuncSrcRGB,
									blendFuncSrcA,
									blendFuncDstRGB,
									blendFuncDstA;
			gl_blend_equation::type	blendModeRGB,
									blendModeA;
			color4u					blendColor;
			bool					blend;
			bool					maskR,
									maskG,
									maskB,
									maskA;
			///
			ColorStateSeparate():
				blendFuncSrcRGB(gl_blend_func::ONE), blendFuncSrcA(gl_blend_func::ONE),
				blendModeRGB(gl_blend_equation::ADD), blendModeA(gl_blend_equation::ADD),
				blendFuncDstRGB(gl_blend_func::ZERO), blendFuncDstA(gl_blend_func::ZERO),
				blend(false), maskR(true), maskG(true), maskB(true), maskA(true)
			{}

			ColorStateSeparate (const ColorState &s) :
				blendFuncSrcRGB(s.blendFuncSrc), blendFuncSrcA(s.blendFuncSrc),
				blendFuncDstRGB(s.blendFuncDst), blendFuncDstA(s.blendFuncDst),
				blendModeRGB(s.blendMode), blendModeA(s.blendMode),
				blend(s.blend), maskR(s.maskR), maskG(s.maskG), maskB(s.maskB), maskA(s.maskA)
			{}

			bool operator == (const ColorStateSeparate &s) const
			{
				return	blendFuncSrcRGB == s.blendFuncSrcRGB and
						blendFuncSrcA == s.blendFuncSrcA and
						blendFuncDstRGB == s.blendFuncDstRGB and
						blendFuncDstA == s.blendFuncDstA and
						blendModeRGB == s.blendModeRGB and
						blendModeA == s.blendModeA and
						All( blendColor == s.blendColor ) and
						blend == s.blend and
						maskR == s.maskR and
						maskG == s.maskG and
						maskB == s.maskB and
						maskA == s.maskA;
			}

			void Invalidate ()
			{
				blendColor		= blendColor + color4u(5);
				blendFuncSrcRGB	= gl_blend_func::type(-1);
				blendFuncSrcA	= gl_blend_func::type(-1);
				blendFuncDstRGB	= gl_blend_func::type(-1);
				blendFuncDstA	= gl_blend_func::type(-1);
				blendModeRGB	= gl_blend_equation::type(-1);
				blendModeA		= gl_blend_equation::type(-1);
				blend			= not blend;
				maskR			= not maskR;
				maskG			= not maskG;
				maskB			= not maskB;
				maskA			= not maskA;
			}

			void Validate ()
			{
				if ( blendFuncSrcA == gl_blend_func::type(-1) )
					blendFuncSrcA = ( blendFuncSrcRGB != gl_blend_func::type(-1) ? blendFuncSrcRGB : gl_blend_func::ONE );

				if ( blendFuncSrcRGB == gl_blend_func::type(-1) )
					blendFuncSrcRGB = gl_blend_func::ONE;
				
				if ( blendFuncDstA == gl_blend_func::type(-1) )
					blendFuncDstA = ( blendFuncDstRGB != gl_blend_func::type(-1) ? blendFuncDstRGB : gl_blend_func::ZERO );

				if ( blendFuncDstRGB == gl_blend_func::type(-1) )
					blendFuncDstRGB = gl_blend_func::ZERO;

				if ( blendModeA == gl_blend_equation::type(-1) )
					blendModeA = ( blendModeRGB != gl_blend_equation::type(-1) ? blendModeRGB : gl_blend_equation::ADD );

				if ( blendModeRGB == gl_blend_equation::type(-1) )
					blendModeRGB = gl_blend_equation::ADD;
			}
		};


		//
		// Depth State
		//
		struct DepthState
		{
			gl_compare::type	func;
			bool				write,
								test;
			///
			DepthState():
				func(gl_compare::LEQUAL), write(true), test(false)
			{}

			bool operator == (const DepthState &s) const
			{
				return	func == s.func and
						write == s.write and
						test == s.test;
			}

			void Invalidate ()
			{
				func	= gl_compare::type(-1);
				write	= not write;
				test	= not test;
			}

			void Validate ()
			{
				if ( func == gl_compare::type(-1) )
					func = gl_compare::LEQUAL;
			}
		};


		//
		// Stencil State
		//
		struct StencilState
		{
			gl_stencil_op::type	sfail,
								dfail,
								dppass;
			gl_compare::type	func;
			uint8				funcRef,
								funcMask,
								mask;
			bool				test;
			///
			StencilState():
				sfail(gl_stencil_op::KEEP), dfail(gl_stencil_op::KEEP),
				dppass(gl_stencil_op::KEEP), func(gl_compare::ALWAYS),
				funcRef(0), funcMask(-1), mask(-1), test(false)
			{}

			bool operator == (const StencilState &s) const
			{
				return	sfail == s.sfail and
						dfail == s.dfail and
						dppass == s.dppass and
						func == s.func and
						funcRef == s.funcRef and
						funcMask == s.funcMask and
						mask == s.mask and
						test == s.test;
			}

			void Invalidate ()
			{
				func	 = gl_compare::type(-1);
				test	 = not test;
				sfail	 = gl_stencil_op::type(-1);
				dfail	 = gl_stencil_op::type(-1);
				dppass	 = gl_stencil_op::type(-1);
				funcRef  = ~ funcRef;
				funcMask = ~ funcMask;
				mask	 = ~ mask;
			}

			void Validate ()
			{
				if ( func == gl_compare::type(-1) )
					func = gl_compare::ALWAYS;

				if ( sfail == gl_stencil_op::type(-1) )
					sfail = gl_stencil_op::KEEP;

				if ( dfail == gl_stencil_op::type(-1) )
					dfail = gl_stencil_op::KEEP;

				if ( dppass == gl_stencil_op::type(-1) )
					dppass = gl_stencil_op::KEEP;
			}
		};


		//
		// Cull Mode
		//
		struct CullMode
		{
			bool	cullBackFace;
			bool	frontFaceCCW;
			bool	enabled;
			///
			CullMode():
				cullBackFace(true), frontFaceCCW(true), enabled(false)
			{}

			bool operator == (const CullMode &s) const
			{
				return	cullBackFace == s.cullBackFace and
						frontFaceCCW == s.frontFaceCCW and
						enabled == s.enabled;
			}

			void Invalidate ()
			{
				cullBackFace	= not cullBackFace;
				frontFaceCCW	= not frontFaceCCW;
				enabled			= not enabled;
			}

			void Validate () {}
		};


		//
		// Polygon Offset
		//
		struct PolygonOffset
		{
			float	factor,
					units;
			bool	enabled;
			///
			PolygonOffset():
				factor(0.0f), units(0.0f), enabled(false)
			{}

			bool operator == (const PolygonOffset &s) const
			{
				return	Equals( factor, s.factor ) and
						Equals( units, s.units ) and
						enabled == s.enabled;
			}

			void Invalidate ()
			{
				factor	= factor + 1000.0f;
				units	= units + 1000.0f;
				enabled	= not enabled;
			}

			void Validate () {}
		};


	// variables
	public:
		ColorStateSeparate	colorState;
		DepthState			depthState;
		StencilState		stencilState;
		CullMode			cullMode;
		PolygonOffset		polygonOffset;


	// methods
	public:
		RenderState () {}

		RenderState & SetColorState (const ColorState &s)			{ colorState = s;		return *this; }
		RenderState & SetColorState (const ColorStateSeparate &s)	{ colorState = s;		return *this; }
		RenderState & SetDepthState (const DepthState &s)			{ depthState = s;		return *this; }
		RenderState & SetStencilState (const StencilState &s)		{ stencilState = s;		return *this; }
		RenderState & SetCullMode (const CullMode &s)				{ cullMode = s;			return *this; }
		RenderState & SetPolygonMode (const PolygonOffset &s)		{ polygonOffset = s;	return *this; }

		void Invalidate ()
		{
			colorState.Invalidate();
			depthState.Invalidate();
			stencilState.Invalidate();
			cullMode.Invalidate();
			polygonOffset.Invalidate();
		}

		void Validate ()
		{
			colorState.Validate();
			depthState.Validate();
			stencilState.Validate();
			cullMode.Validate();
			polygonOffset.Validate();
		}
	};


}	// Graphics
}	// Engine